package com.benshou.bcss.common.j17common;

import com.benshou.bcss.common.j17common.annotation.NotNull;
import lombok.NonNull;

import java.util.Map;
import java.util.Objects;
import java.util.function.*;

/**
 * @author <a href="mailto:wh1zper@qq.com">wh1zper</a>
 */
public class Means {

    private static final Object[] EMPTY_ARRAY = new Object[0];

    public static <T> Predicate<T> always() {
        return ignored -> true;
    }

    public static <T> Predicate<T> never() {
        return ignored -> false;
    }

    public static <U> BinaryOperator<U> useFormer() {
        return (former, latter) -> former;
    }

    public static <U> BinaryOperator<U> useLatter() {
        return (former, latter) -> latter;
    }

    public static <T, K, V> BiConsumer<Map<K, V>, T> uniqKeysMapAccumulator(
            @NotNull Function<? super T, ? extends K> keyMapper,
            @NotNull Function<? super T, @NotNull ? extends V> valueMapper) {
        return (map, element) -> {
            K k = keyMapper.apply(element);
            V v = Objects.requireNonNull(valueMapper.apply(element));
            V u = map.putIfAbsent(k, v);
            if (u != null) throw duplicateKeyException(k, u, v);};
    }

    public static <K, V, M extends Map<K, V>> BinaryOperator<M> uniqKeysMapMerger() {
        return (m1, m2) -> {
            for (Map.Entry<K, V> e : m2.entrySet()) {
                K k = e.getKey();
                V v = Objects.requireNonNull(e.getValue());
                V u = m1.putIfAbsent(k, v);
                if (u != null) throw duplicateKeyException(k, u, v);
            }
            return m1;};
    }

    public static <T> T[] lenientEmptyArray() {
        @SuppressWarnings("unchecked")
        var result = (T[]) EMPTY_ARRAY;
        return result;
    }

    public static <T> T nullElse(T object, T defaultValue) {
        return object == null ? defaultValue : object;
    }

    public static <T> T nullElse(T object, @NonNull Supplier<T> defaultValueSupplier) {
        return object == null ? defaultValueSupplier.get() : object;
    }

    private static IllegalStateException duplicateKeyException(Object k, Object u, Object v) {
        return new IllegalStateException(String.format(
                "Duplicate key %s (attempted merging values %s and %s)",
                k, u, v));
    }
}
